using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using UnityEditor;
using UnityEditor.Rendering;
using UnityEngine;
using Debug = UnityEngine.Debug;

[Serializable]
// ReSharper disable once CheckNamespace
public class ShaderInfo
{
    public string m_ShaderName;
    public string m_ShaderType;
    public List<string> m_GlobalKeywords = new List<string>();
    public List<string> m_LocalKeywords = new List<string>();

    public string m_GlobalKeywordsText;
    public string m_LocalKeywordsText;

    public ShaderBaseInfo m_DefaultInfo = null;
    public ShaderBaseInfo m_PositionVariant = null;
    public ShaderBaseInfo m_VaringVariant = null;

    public ShaderProperties m_ShaderProperties = null;

    public bool Expand { set; get; } = true;
}

[Serializable]
public class ShaderBaseInfo
{
    public int m_WorkRegisters;
    public int m_UniformRegisters;
    public bool m_StackSpilling;
    public float m_Arithmetic16Bit;
    public ShaderCycles m_TotalShaderCycles = new ShaderCycles();
    public ShaderCycles m_ShortestShaderCycles = new ShaderCycles();
    public ShaderCycles m_LongestShaderCycles = new ShaderCycles();

    public class ShaderCycles
    {
        public float m_A;
        public float m_LS;
        public float m_V;
        public float m_T;
        public string m_Bound;
    }
}

public class ShaderProperties
{
    public bool m_HasUniformComputation = false;
    public bool m_HasSideEffects = false;
    public bool m_ModifiesCoverage = false;
    public bool m_UsesLateZSTest = false;
    public bool m_UseLateZSUpdate = false;
    public bool m_ReadColorBuffer = false;
}

public class ShaderComplexityUtils
{
    public const string VERTEX_SHADER_TYPE = "VertexShader";
    public const string FRAGMENT_SHADER_TYPE = "FragmentShader";
    
    #region 请求编译GLSL文件
    
    public class CompileGLSLRequest
    {
        public bool m_IsDone;
        public string m_Error;
        public string m_TempPath;
        public string m_ShaderPath;

        private bool m_FileReady;

        public delegate void OnComplete(string shaderPath, string tempPath, string error);
        public OnComplete onComplete;

        // ReSharper disable once ConvertConstructorToMemberInitializers
        public CompileGLSLRequest()
        {
            m_IsDone = false;
            m_Error = null;
            m_TempPath = string.Empty;
            m_ShaderPath = string.Empty;
            m_FileReady = false;
        }
        
        internal void OnLoading()
        {
            if (m_IsDone && !string.IsNullOrEmpty(m_Error))
            {
                onComplete?.Invoke(m_ShaderPath, m_TempPath, m_Error);
                EditorApplication.update -= this.OnLoading;
                return;
            }
            try
            {
                if (m_FileReady)
                {
                    File.Copy(m_TempPath, m_ShaderPath, true);
                    m_IsDone = true;
                    onComplete?.Invoke(m_ShaderPath, m_TempPath, m_Error);
                    EditorApplication.update -= this.OnLoading;
                    return;
                }
                FileInfo fileInfo = new FileInfo(m_TempPath);
                if (!fileInfo.Exists)
                {
                    m_IsDone = true;
                    m_Error = $"Fail to find file {m_TempPath} in Temp directory";
                    onComplete?.Invoke(m_ShaderPath, m_TempPath, m_Error);
                    EditorApplication.update -= this.OnLoading;
                    return;
                }
                FileStream fileStream = fileInfo.Open(FileMode.Open);
                fileStream.Close();

                m_FileReady = true;
            }
            catch (Exception e)
            {
                Debug.LogError(e);
                m_IsDone = true;
                m_Error = e.ToString();
                onComplete?.Invoke(m_ShaderPath, m_TempPath, m_Error);
                EditorApplication.update -= this.OnLoading;
            }
        }
    }

    public static CompileGLSLRequest RequestCompile(Shader shader, bool includeAllVariants, string destDirectoryPath)
    {
        string projectPath = Application.dataPath.Replace("Assets", "");
        string tempPath = projectPath + "Temp/";
        string tempName = "Compiled-" + shader.name.Replace("/", "-") + ".shader";
        string compiledFilePath = tempPath + tempName;
        if (File.Exists(compiledFilePath))
        {
            File.Delete(compiledFilePath);
        }
        
        MethodInfo openCompiledShaderMethod = typeof(ShaderUtil).GetMethod("OpenCompiledShader", BindingFlags.Static | BindingFlags.NonPublic);
        if (openCompiledShaderMethod != null)
        {
            openCompiledShaderMethod.Invoke(null, new object[] {shader,
                3 /* Custom: */,
                1 << (int)ShaderCompilerPlatform.GLES3x,
                includeAllVariants,
                /*preprocessOnly*/false,
                /*stripLineDirectives*/false});
        }
        
        CompileGLSLRequest req = RequestCompileGLSL(compiledFilePath, destDirectoryPath);
        
        if (openCompiledShaderMethod == null)
        {
            req.m_IsDone = true;
            req.m_Error = $"Fail to get method: ShaderUtil.OpenCompiledShader";
            req.m_TempPath = string.Empty;
            req.m_ShaderPath = string.Empty;
        }
        
        return req;
    }

    public static CompileGLSLRequest RequestCompileGLSL(string compiledFilePath, string destDirectoryPath)
    {
        CompileGLSLRequest req = new CompileGLSLRequest();
        
        req.m_TempPath = compiledFilePath;
        req.m_ShaderPath = Path.Combine(destDirectoryPath, Path.GetFileNameWithoutExtension(compiledFilePath) + ".txt").Replace("\\", "/");
        FileInfo f1 = new FileInfo(req.m_TempPath);
        FileInfo f2 = new FileInfo(req.m_ShaderPath);
        if (f1.FullName == f2.FullName)
        {
            req.m_ShaderPath += ".tmp";
        }
        
        EditorApplication.update += req.OnLoading;

        return req;
    }
    
    #endregion

    private struct ShaderTextInfo
    {
        public string m_VertPath;
        public string m_FragPath;

        public string m_VertShaderName;
        public string m_FragShaderName;

        public string m_VertexShader;
        public string m_FragShader;

        public string m_GlobalKeywords;
        public string m_LocalKeywords;
    }
    
    public static void CalculateMaterialComplexity(Material material, string architecture, string destDirectoryPath, Action<List<ShaderInfo>, string, bool> onComplete)
    {
        if (!Directory.Exists(destDirectoryPath))
        {
            Directory.CreateDirectory(destDirectoryPath);
        }
        CompileGLSLRequest request = RequestCompile(material.shader, false, destDirectoryPath);
        request.onComplete = (shaderPath, _, error) =>
        {
            if (!string.IsNullOrEmpty(error))
            {
                onComplete?.Invoke(null, string.Empty, false);
                return;
            }
            List<ShaderInfo> shaderInfos = CompileToShaderInfos(material, shaderPath, architecture, out string result);
            onComplete?.Invoke(shaderInfos, result, true);
        };
    }
    
    public static void CalculateShaderComplexity(Shader shader, string architecture, string destDirectoryPath, Action<List<ShaderInfo>, string, bool> onComplete)
    {
        if (!Directory.Exists(destDirectoryPath))
        {
            Directory.CreateDirectory(destDirectoryPath);
        }
        CompileGLSLRequest request = RequestCompile(shader, false, destDirectoryPath);
        request.onComplete = (shaderPath, _, error) =>
        {
            if (!string.IsNullOrEmpty(error))
            {
                onComplete?.Invoke(null, string.Empty, false);
                return;
            }
            List<ShaderInfo> shaderInfos = CompileToShaderInfos(null, shaderPath, architecture, out string result);
            onComplete?.Invoke(shaderInfos, result, true);
        };
    }

    // ReSharper disable once OutParameterValueIsAlwaysDiscarded.Local
    public static List<ShaderInfo> CompileToShaderInfos(Material material, string shaderPath, string architecture, out string compileResult)
    {
        compileResult = string.Empty;
        
        string text = File.ReadAllText(shaderPath);
            
        int curIndex = 0;
        
        string vertexBeginFlag = "#ifdef VERTEX";
        string fragBeginFlag = "#ifdef FRAGMENT";
        string endFlag = "#endif";
        int shaderIndex = 1;
        
        string path = shaderPath.Replace("\\", "/");
        string fileName = Path.GetFileNameWithoutExtension(path);
        path = path.Substring(0, path.LastIndexOf("/", StringComparison.Ordinal));

        List<ShaderTextInfo> shaderTextInfos = new List<ShaderTextInfo>();

        int minDiff = int.MaxValue;
        
        string keywordFlag = "Global Keywords:";
        if (!text.Contains(keywordFlag))
        {
            keywordFlag = "Keywords:";
            text = text.Replace("300 es", "310 es");
        }
        
        while ((curIndex = text.IndexOf(keywordFlag, curIndex, StringComparison.Ordinal)) >= 0)
        {
            int globalKWEnd = text.IndexOf("\n", curIndex, StringComparison.Ordinal);
            string globalKW = text.Substring(curIndex, globalKWEnd - curIndex);

            string localKW = string.Empty;
            if (keywordFlag == "Global Keywords:")
            {
                int localKWBegin = text.IndexOf("Local Keywords:", curIndex, StringComparison.Ordinal);
                int localKWEnd = text.IndexOf("\n", localKWBegin, StringComparison.Ordinal);
                localKW = text.Substring(localKWBegin, localKWEnd - localKWBegin);
            }
            
            int vertexBeginIndex = text.IndexOf(vertexBeginFlag, curIndex, StringComparison.Ordinal) + vertexBeginFlag.Length;
            int fragBeginIndex = text.IndexOf(fragBeginFlag, curIndex, StringComparison.Ordinal) + fragBeginFlag.Length;
            int nextIndex = text.IndexOf(keywordFlag, fragBeginIndex, StringComparison.Ordinal);

            if (!IsKeywordsMatch(material, globalKW, localKW, out int matchDiff))
            {
                curIndex = fragBeginIndex;
                if (shaderIndex > 100)
                {
                    break;
                }
                ++shaderIndex;
                continue;
            }
            if (material != null && minDiff <= matchDiff)
            {
                curIndex = fragBeginIndex;
                if (shaderIndex > 100)
                {
                    break;
                }
                ++shaderIndex;
                continue;
            }
            minDiff = matchDiff;
            
            if (vertexBeginIndex < fragBeginIndex)
            {
                int vertexEndIndex = text.LastIndexOf(endFlag, fragBeginIndex, StringComparison.Ordinal);
                string vertexShader = text.Substring(vertexBeginIndex + 1, vertexEndIndex - vertexBeginIndex - 1);
                vertexShader = vertexShader + "\n// " + globalKW;
                vertexShader = vertexShader + "\n// " + localKW;
                GuaranteeVersionIsFirstStat(ref vertexShader);
                string shaderName1 = fileName + "_" + shaderIndex + ".vert";
                string vertPath = Path.Combine(path, shaderName1).Replace("\\", "/");
                
                // File.WriteAllText(vertPath, vertexShader);
                //
                // AssetDatabase.Refresh();
                // MaliOC(vertPath, shaderName1, "VertexShader", globalKW, localKW);

                int fragEndIndex = nextIndex < 0
                    ? text.LastIndexOf(endFlag, text.Length - 1, StringComparison.Ordinal)
                    : text.LastIndexOf(endFlag, nextIndex, StringComparison.Ordinal);
                string fragShader = text.Substring(fragBeginIndex + 1, fragEndIndex - fragBeginIndex - 1);
                fragShader = fragShader + "\n// " + globalKW;
                fragShader = fragShader + "\n// " + localKW;
                GuaranteeVersionIsFirstStat(ref fragShader);
                string shaderName2 = fileName + "_" + shaderIndex + ".frag";
                string fragPath = Path.Combine(path, shaderName2).Replace("\\", "/");
                
                ShaderTextInfo shaderTextInfo = new ShaderTextInfo()
                {
                    m_VertexShader = vertexShader,
                    m_FragShader = fragShader,
                    m_VertShaderName = shaderName1,
                    m_FragShaderName = shaderName2,
                    m_VertPath = vertPath,
                    m_FragPath = fragPath,
                    m_GlobalKeywords = globalKW,
                    m_LocalKeywords = localKW,
                };
                if (material != null)
                {
                    shaderTextInfos.Clear();
                }
                shaderTextInfos.Add(shaderTextInfo);
            }
            else
            {
                int fragEndIndex = text.LastIndexOf(endFlag, vertexBeginIndex, StringComparison.Ordinal);
                string fragShader = text.Substring(fragBeginIndex + 1, fragEndIndex - fragEndIndex - 1);
                fragShader = fragShader + "\n// " + globalKW;
                fragShader = fragShader + "\n// " + localKW;
                GuaranteeVersionIsFirstStat(ref fragShader);
                string shaderName1 = fileName + "_" + shaderIndex + ".frag";
                string fragPath = Path.Combine(path, shaderName1).Replace("\\", "/");

                int vertEndIndex = nextIndex < 0
                    ? text.LastIndexOf(endFlag, text.Length - 1, StringComparison.Ordinal)
                    : text.LastIndexOf(endFlag, nextIndex, StringComparison.Ordinal);
                string vertShader = text.Substring(vertexBeginIndex + 1, vertEndIndex - vertexBeginIndex - 1);
                vertShader = vertShader + "\n// " + globalKW;
                vertShader = vertShader + "\n// " + localKW;
                GuaranteeVersionIsFirstStat(ref vertShader);
                string shaderName2 = fileName + "_" + shaderIndex + ".vert";
                string vertPath = Path.Combine(path, shaderName2).Replace("\\", "/");

                ShaderTextInfo shaderTextInfo = new ShaderTextInfo()
                {
                    m_VertexShader = vertShader,
                    m_FragShader = fragShader,
                    m_VertShaderName = shaderName2,
                    m_FragShaderName = shaderName1,
                    m_VertPath = vertPath,
                    m_FragPath = fragPath,
                    m_GlobalKeywords = globalKW,
                    m_LocalKeywords = localKW,
                };
                if (material != null)
                {
                    shaderTextInfos.Clear();
                }
                shaderTextInfos.Add(shaderTextInfo);
            }

            curIndex = fragBeginIndex;
            if (shaderIndex > 100)
            {
                break;
            }

            ++shaderIndex;
        }

        List<ShaderInfo> shaderInfos = new List<ShaderInfo>();
        for (int i = 0; i < shaderTextInfos.Count; ++i)
        {
            ShaderTextInfo shaderTextInfo = shaderTextInfos[i];
            
            File.WriteAllText(shaderTextInfo.m_VertPath, shaderTextInfo.m_VertexShader);
            AssetDatabase.Refresh();
            ShaderInfo vertShaderInfo = MaliOC(shaderTextInfo.m_VertPath, shaderTextInfo.m_VertShaderName, VERTEX_SHADER_TYPE,
                shaderTextInfo.m_GlobalKeywords, shaderTextInfo.m_LocalKeywords, architecture, out string vCompileResult);
            shaderInfos.Add(vertShaderInfo);
            compileResult += vCompileResult;
            
            File.WriteAllText(shaderTextInfo.m_FragPath, shaderTextInfo.m_FragShader);
            AssetDatabase.Refresh();
            ShaderInfo fragShaderInfo = MaliOC(shaderTextInfo.m_FragPath, shaderTextInfo.m_FragShaderName, FRAGMENT_SHADER_TYPE,
                shaderTextInfo.m_GlobalKeywords, shaderTextInfo.m_LocalKeywords, architecture, out string fCompileResult);
            shaderInfos.Add(fragShaderInfo);
            compileResult += fCompileResult;
        }
        
        return shaderInfos;
    }

    private static ShaderInfo MaliOC(string path, string shaderName, string shaderType, string globalKW, string localKW, string architecture, out string compileResult)
    {
        ShaderInfo shaderInfo = null;
        
        int assetIndex = Application.dataPath.LastIndexOf("Assets", StringComparison.Ordinal);
        string projectDirectory = Application.dataPath.Substring(0, assetIndex).Replace('\\', '/');
        
        string maliocPath = projectDirectory + "Tools/mali_offline_compiler/malioc.exe";

        string command = $"\"{maliocPath}\" -c {architecture} \"{path}\"";

        ProcessStartInfo startInfo = new ProcessStartInfo()
        {
            FileName = "cmd.exe",
            RedirectStandardOutput = true,
            RedirectStandardError = true,
            RedirectStandardInput = true,
            UseShellExecute = false,
            CreateNoWindow = true
        };
        Process process = new Process();
        process.StartInfo = startInfo;
        process.Start();

        process.StandardInput.WriteLine(command + "&exit");
        process.StandardInput.AutoFlush = true;

        string output = process.StandardOutput.ReadToEnd();
        string error = process.StandardError.ReadToEnd();
        
        process.WaitForExit();
        process.Close();
        
        compileResult = string.Empty;
        compileResult += "■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■\r\n";
        compileResult += globalKW + "\r\n";
        compileResult += localKW + "\r\n";
        compileResult += "Shader名称：" + shaderName + "\r\n";
        compileResult += "■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■\r\n";
        if (!string.IsNullOrEmpty(output))
        {
            shaderInfo = ParseShaderInfo(output, shaderName, shaderType, globalKW, localKW);

            output = output.Replace("Work registers:", "工作寄存器：");
            output = output.Replace("Uniform registers:", "Uniform寄存器：");
            output = output.Replace("Stack spilling:", "是否有变量被放置到栈内存中：");
            output = output.Replace("Total instruction cycles:", "指令的累积执行Cycles：    ");
            output = output.Replace("Shortest path cycles:", "最短控制流路径Cycles：");
            output = output.Replace("Longest path cycles:", "最长控制流路径Cycles：");
        }
        compileResult += output + "\r\n";
        if (!string.IsNullOrEmpty(error))
        {
            compileResult += "Error:\r\n" + error + "\r\n";
        }

        return shaderInfo;
    }
    
    /// <summary>
    /// 当前读取解析的状态
    /// </summary>
    private enum ReadState
    {
        None,
        MainShader,
        DefaultVariant,
        PositionVariant,
        VaryingVariant,
        ShaderProperties,
    }
    
    /// <summary>
    /// 根据Shader文本解析ShaderInfo数据，用于在编辑器里显示
    /// </summary>
    /// <param name="ocResult">Shader文本</param>
    /// <param name="shaderName">Shader变体名称</param>
    /// <param name="shaderType">Shader类型（VS/PS）</param>
    /// <param name="globalKeyword">全局关键字</param>
    /// <param name="localKeyword">局部关键字</param>
    private static ShaderInfo ParseShaderInfo(string ocResult, string shaderName, string shaderType, string globalKeyword, string localKeyword)
    {
        ShaderInfo shaderInfo = new ShaderInfo();
        shaderInfo.m_ShaderName = shaderName;
        shaderInfo.m_ShaderType = shaderType;

        string[] gkwDatas = globalKeyword.Split(new[] {':'}, StringSplitOptions.RemoveEmptyEntries);
        if (gkwDatas.Length >= 2)
        {
            string[] gkws = gkwDatas[1].Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
            shaderInfo.m_GlobalKeywords.AddRange(gkws);

            bool first = true;
            foreach (string keyword in shaderInfo.m_GlobalKeywords)
            {
                if (!first)
                {
                    shaderInfo.m_GlobalKeywordsText += ", ";
                }

                shaderInfo.m_GlobalKeywordsText += keyword;
                first = false;
            }
        }

        string[] lkwDatas = localKeyword.Split(new[] {':'}, StringSplitOptions.RemoveEmptyEntries);
        if (lkwDatas.Length >= 2)
        {
            string[] lkws = lkwDatas[1].Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
            shaderInfo.m_LocalKeywords.AddRange(lkws);

            bool first = true;
            foreach (string keyword in shaderInfo.m_LocalKeywords)
            {
                if (!first)
                {
                    shaderInfo.m_LocalKeywordsText += ", ";
                }

                shaderInfo.m_LocalKeywordsText += keyword;
                first = false;
            }
        }

        ReadState readState = ReadState.None;

        int pvIndex = ocResult.IndexOf("Position variant", StringComparison.Ordinal);
        int vvIndex = ocResult.IndexOf("Varying variant", StringComparison.Ordinal);

        ShaderBaseInfo currentBaseInfo = null;
        List<string> currentCyclesFormat = new List<string>();

        using (StringReader reader = new StringReader(ocResult))
        {
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                switch (line)
                {
                    case "Main shader":
                    {
                        readState = ReadState.MainShader;
                        if (pvIndex < 0 && vvIndex < 0)
                        {
                            readState = ReadState.DefaultVariant;
                            currentBaseInfo = new ShaderBaseInfo();
                            shaderInfo.m_DefaultInfo = currentBaseInfo;
                        }

                        break;
                    }
                    case "Position variant":
                    {
                        readState = ReadState.PositionVariant;
                        currentBaseInfo = new ShaderBaseInfo();
                        shaderInfo.m_PositionVariant = currentBaseInfo;
                        break;
                    }
                    case "Varying variant":
                    {
                        readState = ReadState.VaryingVariant;
                        currentBaseInfo = new ShaderBaseInfo();
                        shaderInfo.m_VaringVariant = currentBaseInfo;
                        break;
                    }
                    case "Shader properties":
                    {
                        readState = ReadState.ShaderProperties;
                        shaderInfo.m_ShaderProperties = new ShaderProperties();
                        break;
                    }
                }

                switch (readState)
                {
                    case ReadState.DefaultVariant:
                    case ReadState.PositionVariant:
                    case ReadState.VaryingVariant:
                    {
                        if (currentBaseInfo == null)
                        {
                            break;
                        }

                        string[] datas = line.Split(new[] {':'}, StringSplitOptions.RemoveEmptyEntries);
                        if (datas.Length <= 0)
                        {
                            break;
                        }

                        string key = datas[0];
                        switch (key)
                        {
                            case "Work registers":
                            {
                                string[] innerDatas = datas[1].Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
                                int.TryParse(innerDatas[0], out currentBaseInfo.m_WorkRegisters);
                                break;
                            }
                            case "Uniform registers":
                            {
                                string[] innerDatas = datas[1].Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
                                int.TryParse(innerDatas[0], out currentBaseInfo.m_UniformRegisters);
                                break;
                            }
                            case "Stack spilling":
                            {
                                string[] innerDatas = datas[1].Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
                                bool.TryParse(innerDatas[0], out currentBaseInfo.m_StackSpilling);
                                break;
                            }
                            case "16-bit arithmetic":
                            {
                                string[] innerDatas = datas[1].Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
                                string num = innerDatas[0];
                                num = num.Replace("%", "");
                                if (float.TryParse(num, out float n))
                                {
                                    currentBaseInfo.m_Arithmetic16Bit = n * 1e-2f;
                                }

                                break;
                            }
                            case "Total instruction cycles":
                            {
                                string[] innerDatas = datas[1].Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
                                ParseCycles(currentCyclesFormat, currentBaseInfo.m_TotalShaderCycles, innerDatas);
                                break;
                            }
                            case "Shortest path cycles":
                            {
                                string[] innerDatas = datas[1].Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
                                ParseCycles(currentCyclesFormat, currentBaseInfo.m_ShortestShaderCycles, innerDatas);
                                break;
                            }
                            case "Longest path cycles":
                            {
                                string[] innerDatas = datas[1].Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
                                ParseCycles(currentCyclesFormat, currentBaseInfo.m_LongestShaderCycles, innerDatas);
                                break;
                            }
                        }

                        if (key.Contains("A "))
                        {
                            currentCyclesFormat.Clear();
                            string[] innerDatas = key.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
                            currentCyclesFormat.AddRange(innerDatas);
                        }

                        break;
                    }
                    case ReadState.ShaderProperties:
                    {
                        if (shaderInfo.m_ShaderProperties == null)
                        {
                            break;
                        }

                        string[] datas = line.Split(new[] {':'}, StringSplitOptions.RemoveEmptyEntries);
                        if (datas.Length <= 0)
                        {
                            break;
                        }

                        string key = datas[0];
                        switch (key)
                        {
                            case "Has uniform computation":
                            {
                                bool.TryParse(datas[1], out shaderInfo.m_ShaderProperties.m_HasUniformComputation);
                                break;
                            }
                            case "Has side-effects":
                            {
                                bool.TryParse(datas[1], out shaderInfo.m_ShaderProperties.m_HasSideEffects);
                                break;
                            }
                            case "Modifies coverage":
                            {
                                bool.TryParse(datas[1], out shaderInfo.m_ShaderProperties.m_ModifiesCoverage);
                                break;
                            }
                            case "Uses late ZS test":
                            {
                                bool.TryParse(datas[1], out shaderInfo.m_ShaderProperties.m_UsesLateZSTest);
                                break;
                            }
                            case "Uses late ZS update":
                            {
                                bool.TryParse(datas[1], out shaderInfo.m_ShaderProperties.m_UseLateZSUpdate);
                                break;
                            }
                            case "Reads color buffer":
                            {
                                bool.TryParse(datas[1], out shaderInfo.m_ShaderProperties.m_ReadColorBuffer);
                                break;
                            }
                        }

                        break;
                    }
                }
            }
        }
        
        return shaderInfo;
    }
    
    private static void ParseCycles(List<string> currentCyclesFormat, ShaderBaseInfo.ShaderCycles shaderCycles, string[] datas)
    {
        for (int i = 0; i < currentCyclesFormat.Count; ++i)
        {
            string format = currentCyclesFormat[i];
            string data = datas[i];
            if (i < currentCyclesFormat.Count - 1)
            {
                switch (format)
                {
                    case "A":
                        float.TryParse(data, out shaderCycles.m_A);
                        break;
                    case "LS":
                        float.TryParse(data, out shaderCycles.m_LS);
                        break;
                    case "V":
                        float.TryParse(data, out shaderCycles.m_V);
                        break;
                    case "T":
                        float.TryParse(data, out shaderCycles.m_T);
                        break;
                }
            }
            else
            {
                shaderCycles.m_Bound = data;
            }
        }
    }

    private static bool IsKeywordsMatch(Material material, string globalKW, string localKW, out int matchDiff)
    {
        matchDiff = 0;
        if (material == null)
        {
            return true;
        }
        List<string> globalKeywords = new List<string>();
        List<string> localKeywords = new List<string>();
            
        string[] gkwDatas = globalKW.Split(new[] {':'}, StringSplitOptions.RemoveEmptyEntries);
        if (gkwDatas.Length >= 2)
        {
            string[] gkws = gkwDatas[1].Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
            globalKeywords.AddRange(gkws);
        }
            
        string[] lkwDatas = localKW.Split(new[] {':'}, StringSplitOptions.RemoveEmptyEntries);
        if (lkwDatas.Length >= 2)
        {
            string[] lkws = lkwDatas[1].Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
            localKeywords.AddRange(lkws);
        }

        string[] enabledKeywords = new string[material.enabledKeywords.Length];
        for (int i = 0; i < enabledKeywords.Length; ++i)
        {
            enabledKeywords[i] = material.enabledKeywords[i].name;
        }
        matchDiff = globalKeywords.Count + localKeywords.Count - enabledKeywords.Length;

        return KeywordMatch(enabledKeywords, globalKeywords, localKeywords);
    }
    
    /// <summary>
    /// 保证#version是第一句话
    /// </summary>
    /// <param name="shaderText">Shader文本</param>
    private static void GuaranteeVersionIsFirstStat(ref string shaderText)
    {
        int versionIndex = shaderText.IndexOf("#version", StringComparison.Ordinal);
        if (versionIndex < 0)
        {
            return;
        }

        shaderText = shaderText.Substring(versionIndex);
    }

    private static bool KeywordMatch(string[] matKeywords, List<string> globalKeywords, List<string> localKeywords)
    {
        globalKeywords.Remove("<none>");
        localKeywords.Remove("<none>");
        if (matKeywords.Length == 0)
        {
            return globalKeywords.Count == 0 && localKeywords.Count == 0;
        }
        
        int matchCount = 0;
        for (int i = 0; i < matKeywords.Length; ++i)
        {
            string matKeyword = matKeywords[i];
            bool match = false;
            for (int j = 0; j < globalKeywords.Count; ++j)
            {
                string kw = globalKeywords[j];
                if (matKeyword == kw)
                {
                    match = true;
                    break;
                }
            }
            for (int j = 0; j < localKeywords.Count && !match; ++j)
            {
                string kw = localKeywords[j];
                if (matKeyword == kw)
                {
                    match = true;
                    break;
                }
            }
            if (match)
            {
                ++matchCount;
            }
        }

        return matchCount == matKeywords.Length;
    }
}